package demo11

import "fmt"

// --------------------------------场景1-----------------------------------

// ImageFlyweightFactory | ImageFlyweight | ImageViewer
// 来看一下官方案例，
// 分析：该设计模式与前面的原型设计模式有点类似，原型设计模式是面向接口，该设计模式是面向结构体

// 有点类似创建型模式 
// 定义管理ImageFllyweight工厂
type ImageFlyweightFactory struct{
	maps map[string]*ImageFlyweight
}
 
var imageFlyweightFactory *ImageFlyweightFactory
// 构造初始化
func NewImageFlyweightFactory() *ImageFlyweightFactory{
	if imageFlyweightFactory == nil {
		imageFlyweightFactory = &ImageFlyweightFactory{
			maps:make(map[string]*ImageFlyweight),
		}
	}
	return imageFlyweightFactory
}
func (i *ImageFlyweightFactory)Get(name string) *ImageFlyweight{
	imageFlyweight := i.maps[name]
	if imageFlyweight == nil {
		imageFlyweight = NewImageFlyweight(name)
		i.maps[name] = imageFlyweight
	}
	return imageFlyweight
}

type ImageFlyweight struct{
	data string
}

func NewImageFlyweight(data string) *ImageFlyweight{
	return &ImageFlyweight{
		data:data,
	}
}

func (i *ImageFlyweight)Data() string {
	return i.data
}

type ImageViewer struct{
	*ImageFlyweight
}

func NewImageViewer(name string) *ImageViewer{
	imageFlyweight := NewImageFlyweightFactory().Get(name)
	return &ImageViewer{
		ImageFlyweight:imageFlyweight,
	}
}

func (i *ImageFlyweight)Display(){
	fmt.Println(i.Data())
}

// --------------------------------场景2-----------------------------------
// 分析，享元和原型区别
// 原型：map 里面是接口，也就是说可以管理不同类型的实例，简单的说就是不同的对象,然后从里面取，如果没有的话就创建
// 享元：map 里面是结构体 ，也就是说只能管理同一类的实例，简单的说，实例化同一类型的实例然后从里面去，如果没有的话就创建

// 原型和享元的场景 个人理解，并且太大的区分，看个人的情况来运用，
// 但是原型有一个更方便的地方相对于享元 ，那就是自我克隆，自我克隆的方法减少了实例的消耗，其实我刚刚也说了都差不多，那么享元模式添加上
// 抽象的克隆方法，那么结果就跟原型设计模式基本上差不多了

// 练习
// 需求：有一个工厂，只能生产飞机，但是每个飞机都有自己的编号
// 分析：飞机类  飞机编号是分类属性，工厂是飞机的管理中心
// DDD:  plane   number int     planeFactory | 这里的分类不是抽象的，如果按照该模式来设计，那么就是一个实例模型
// 需要一个审计人员，，来可以检测飞机的信息 planeViewer

// 工厂
type PlaneFactory struct{
	planes map[int]*Plane
}

// 管理
var planeFactory *PlaneFactory

// 初始化
func NewplaneFactory() *PlaneFactory{
	if planeFactory == nil {
		planeFactory = &PlaneFactory{
			planes:make(map[int]*Plane),
		}
	}
	return planeFactory
}

// 获取的方法 | 工厂生产飞机，那么工厂肯定知道飞机的型号，所以这个方法写到工厂里，而不是写在审计员
// 这里一定要区分OOP的思想，审计员可以调用来获取
func (p *PlaneFactory)Get(number int) *Plane{
	plane := p.planes[number]
	// 这里注意与python不一样的地方，如果没有则返回nil 不是异常
	if plane == nil{
		plane = NewPlane(number)
		p.planes[number] = plane
	}
	return plane
}

// 飞机实例
type Plane struct{
	// 飞机属性
	number int
}
// 飞机初始化
func NewPlane(number int) *Plane{
	return &Plane{
		number:number,
	}
}
// 飞机属性查看
// 这里飞机可以查看自己的属性，工厂也可以通过遍历获取到
func (p *Plane)Data() int{
	return p.number
}

// 设计 检测员
type planeViewer struct{
	// 由于 检测的是飞机实例，所以这里嵌套的是飞机
	// 如果钻牛角的话，工厂也是可以的
	*Plane
}

// 检测某一个飞机，传入飞机编号
func NewPlaneView(number int) *planeViewer{
	// 检测某个飞机
	plane := NewplaneFactory().Get(number)
	return &planeViewer{
		Plane:plane,
	}
}

func (p *planeViewer)Display() {
	fmt.Println(p.Data())
}

// --------------------------------场景3-----------------------------------
// 需求：DB 连接池
// 分析：某个DB的请求比较频繁
// DDD: DBJDBC 实例| DBQueue 连接池 | DBObj 某个对象

// 通过 DBObj 获取实例对象

type DBQueue struct{
	dbs map[string]*DBJDBC
}
var dbQueue *DBQueue // 连接池
// 初始化
func NewDBQueue() *DBQueue{
	// 类似单利
	if dbQueue ==nil{
		dbQueue = &DBQueue{
			dbs:make(map[string]*DBJDBC),
		}
	}
	return dbQueue
}
// get
func (d *DBQueue)Get(name string) *DBJDBC{
	dbJDBC := d.dbs[name]
	if dbJDBC == nil {
		dbJDBC = NewDBJDBC(name)
		d.dbs[name] = dbJDBC
	}
	return dbJDBC
}

type DBJDBC struct{
	name string
}

func NewDBJDBC(name string) *DBJDBC{
	// 这里如果涉及多个不同的实例参数是一个挑战
	// 简单的办法就是通过配置来实现
	// 动态加载不同的办法可以通过 参数来判断
	return &DBJDBC{
		name:name,
	}
}

func (d *DBJDBC)Data() string {
	return d.name
}

// 获取方法
type DBObj struct{
	*DBJDBC
}
func NewDBObj(name string) *DBObj{
	obj := NewDBQueue().Get(name)
	return &DBObj{
		DBJDBC:obj,
	}
}	
func (d *DBObj)Display(){
	// 这里使用的d.Data()而不是d.name
	fmt.Println("建立连接:",d.Data())
}


// 简答回顾今天
// 测试的分类  单元--组件--集成--端对端 | 桩 契约测试， 测试工具Docker ,Gradle,cucumber等
// 安全性，日志跟踪，可观测性等
// 代理模式用在API 上较多，适配器用在服务到DB之间，
// 使用到的技术:Kafka ,DB,
// 享元设计模式
// 结构体,主要是对同一类实例进行管理 